import struct


class PSAMCardIssuer:
    def __init__(self, connection):
        self.connection = connection
        
    def send_apdu(self, apdu):
        """发送APDU指令并返回响应"""
        response, sw1, sw2 = self.connection.transmit(apdu)
        return response, (sw1 << 8) | sw2
    
    def select_file(self, file_id):
        """选择文件"""
        apdu = [0x00, 0xA4, 0x00, 0x00] + [len(file_id)] + file_id
        return self.send_apdu(apdu)
    
    def get_challenge(self, length=4):
        """获取随机数"""
        apdu = [0x00, 0x84, 0x00, 0x00, length]
        return self.send_apdu(apdu)
    
    def derive_key(self, key_id, div_data_len, div_serial, div_area):
        """分散密钥"""
        apdu = [0x80, 0x1A] + key_id + [div_data_len] + div_serial + div_area
        return self.send_apdu(apdu)
    
    def calculate_mac(self, data):
        """计算MAC"""
        apdu = [0x80, 0xFA, 0x05, 0x00, len(data)] + data
        return self.send_apdu(apdu)
    
    def update_file(self, fid, start, data, mac_flag=0x04, key_id=None, 
                   div_serial=None, div_area=None, div_data_len=None):
        """更新文件内容"""
        # 构造写入指令
        inst_data = [mac_flag, 0xD6, fid | 0x80, start, len(data)] + data
        
        # 计算MAC数据
        cal_mac_data = inst_data + [0x80]
        while len(cal_mac_data) % 16 != 0:
            cal_mac_data.append(0x00)
        
        # 1. 进入PSAM卡DF01文件
        self.select_file([0xDF, 0x01])
        
        # 2. 分散密钥
        self.derive_key(key_id, div_data_len, div_serial, div_area)
        
        # 3. 获取随机数
        random, _ = self.get_challenge()
        random += [0] * 12  # 补12字节0x00
        
        # 4. 计算MAC
        mac_data = [len(cal_mac_data) + 16] + random + cal_mac_data
        mac, _ = self.calculate_mac(mac_data)
        
        # 5. 执行写文件
        write_apdu = inst_data + mac
        return self.send_apdu(write_apdu)
    
    def issue_tag(self, tag_vendor_code, tag_serial, system_info, 
                  vehicle_info, card_vendor_code, card_serial, 
                  card_16_data, card_15_data):
        """标签二次发行主流程"""
        try:
            # 1. 唤醒标签 (假设已唤醒)
            
            # 2. 进入标签3F00根目录
            self.select_file([0x3F, 0x00])
            
            # 3. 更新系统信息
            div_area = tag_vendor_code * 2
            div_serial = tag_serial
            self.update_file(
                fid=0x01,
                start=0x00,
                data=system_info,
                key_id=[0x26, 0x41],
                div_serial=div_serial,
                div_area=div_area,
                div_data_len=0x08
            )
            
            # 4. 进入标签DF01文件
            self.select_file([0xDF, 0x01])
            
            # 5. 更新车辆信息
            self.update_file(
                fid=0x01,
                start=0x00,
                data=vehicle_info,
                key_id=[0x26, 0x42],
                div_serial=div_serial,
                div_area=div_area,
                div_data_len=0x08
            )
            
            # 6. 进入卡片3F00目录
            self.select_file([0x3F, 0x00])
            
            # 7. 更新卡片0016文件
            div_area = card_vendor_code * 2
            div_serial = card_serial
            self.update_file(
                fid=0x16,
                start=0x00,
                data=card_16_data,
                key_id=[0x26, 0x43],
                div_serial=div_serial,
                div_area=div_area,
                div_data_len=0x08
            )
            
            # 8. 进入卡片1001目录
            self.select_file([0x10, 0x01])
            
            # 9. 更新卡片0015文件
            self.update_file(
                fid=0x15,
                start=0x00,
                data=card_15_data,
                key_id=[0x26, 0x44],
                div_serial=div_serial,
                div_area=div_area,
                div_data_len=0x08
            )
            
            return True, "标签二次发行成功"
            
        except Exception as e:
            return False, f"发行失败: {str(e)}"

# 示例用法
if __name__ == "__main__":
    # 初始化读卡器
    reader = readers()[0]
    connection = reader.createConnection()
    connection.connect()

    # 创建发行器实例
    issuer = PSAMCardIssuer(connection)

    # 准备发行数据
    tag_vendor_code = [0x11, 0x22, 0x33, 0x44]  # 4字节标签供应商代码
    tag_serial = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]  # 8字节标签序列号
    system_info = [0x00]*43  # 43字节系统信息
    vehicle_info = [0x00]*79  # 79字节车辆信息
    card_vendor_code = [0x55, 0x66, 0x77, 0x88]  # 4字节卡片供应商代码
    card_serial = [0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10]  # 8字节卡片编号
    card_16_data = [0x00]*55  # 55字节0016文件数据
    card_15_data = [0x00]*50  # 50字节0015文件数据

    # 执行发行
    success, message = issuer.issue_tag(
        tag_vendor_code, tag_serial, system_info, 
        vehicle_info, card_vendor_code, card_serial,
        card_16_data, card_15_data
    )

    print(f"发行结果: {success}, 消息: {message}")